package com.wejoy.video.middle.station.data.service.system.impl;

import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.wejoy.video.middle.station.data.constant.RedisKeys;
import com.wejoy.video.middle.station.data.domain.system.SysDept;
import com.wejoy.video.middle.station.data.domain.system.SysUser;
import com.wejoy.video.middle.station.data.enums.dict.EnumStatus;
import com.wejoy.video.middle.station.data.exception.BusinessException;
import com.wejoy.video.middle.station.data.mapper.system.ISysUserMapper;
import com.wejoy.video.middle.station.data.service.common.IIdWorkerService;
import com.wejoy.video.middle.station.data.service.common.IVerifyCodeService;
import com.wejoy.video.middle.station.data.service.evs.IEvsHttpService;
import com.wejoy.video.middle.station.data.service.system.ISysDeptService;
import com.wejoy.video.middle.station.data.service.system.ISysDeptUserService;
import com.wejoy.video.middle.station.data.service.system.ISysUserService;
import com.wejoy.video.middle.station.data.util.WJAssert;
import com.wejoy.video.middle.station.data.util.baiduevs.EvsApiHttpUtils;
import com.wejoy.video.middle.station.data.util.passwordEncoder.BCryptPasswordEncoderUtils;
import com.wejoy.video.middle.station.data.vo.evsapi.DeparmentInfoListVO;
import com.wejoy.video.middle.station.data.vo.evsapi.EvsApiLoginVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpSession;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 系统--用户表
 *
 * @author admin
 * @date 2021年10月19日 15:07:06
 */
@Service
public class SysUserServiceImpl implements ISysUserService {
    @Autowired
    private ISysUserMapper sysUserMapper;
    @Autowired
    private IIdWorkerService idWorkerService;
    @Autowired
    private BCryptPasswordEncoderUtils bCryptPasswordEncoder;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    @Lazy
    private ISysDeptUserService sysDeptUserService;
    @Autowired
    @Lazy
    private ISysDeptService sysDeptService;

    @Autowired
    private IEvsHttpService evsHttpService;

    @Autowired
    private IVerifyCodeService verifyCodeService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(SysUser req, String operator) {
        req.setOperator(operator);
        String password = req.getPassword();
        if (StringUtils.isNotBlank(password)) {
            req.setPassword(bCryptPasswordEncoder.encode(password));
        }
        // 检查用户名
//        if (isEmailExist(req)) {
//            throw new BusinessException("邮箱已存在");
//        }
        // 检查邮箱
        if (null == req.getId()) {
            if (!isUserPhoneExist(req)) {
                throw new BusinessException("手机号已存在");
            }
            insertSelective(req);
        } else {
            SysUser oneById = getOneById(req.getId());
            // 检查手机号
            if(!oneById.getPhone().equals(req.getPhone())){
                if (!isUserPhoneExist(req)) {
                    throw new BusinessException("手机号已存在");
                }
            }
            updateSelectiveById(req);
        }
        sysDeptUserService.saveUserDept(req, operator);
    }

    @Override
    public int insertSelective(SysUser req) {
        req.setId(idWorkerService.getNextId());
        req.setStatus(EnumStatus.NEW.getCode());
        checkStringLength(req, false);
        return sysUserMapper.insertSelective(req);
    }

    @Override
    public int updateSelectiveById(SysUser sysUser) {
        WJAssert.notNull(sysUser, "入参对象不能为空");
        WJAssert.notNull(sysUser.getId(), "id参数错误");
        SysUser where = new SysUser();
        where.setId(sysUser.getId());
        return sysUserMapper.updateSelective(sysUser, where);
    }

    @Override
    public int getCount(SysUser sysUser) {
        checkStringLength(sysUser, true);
        return sysUserMapper.getCount(sysUser);
    }

    @Override
    public int getDataPermissionCount(SysUser sysUser) {
        checkStringLength(sysUser, true);
        return sysUserMapper.getDataPermissionCount(sysUser);
    }

    @Override
    public SysUser getOneById(Long id) {
        WJAssert.notNull(id, "id参数错误");
        SysUser where = new SysUser();
        where.setId(id);
        return sysUserMapper.getOne(where);
    }

    @Override
    public List<SysUser> getList(SysUser sysUser) {
        checkStringLength(sysUser, true);
        return sysUserMapper.getList(sysUser);
    }

    @Override
    public List<SysUser> getPageList(SysUser sysUser) {
        checkStringLength(sysUser, true);
        return sysUserMapper.getPageList(sysUser)
                .parallelStream()
                .peek(item -> {
                    if (StringUtils.isNotBlank(item.getTag())) {
                        item.setTagList(Arrays.asList(item.getTag().split(",")));
                    }
                })
                .peek(item -> {
                    List<List<Long>> result = new ArrayList<>();
                    List<String> deptNameList = new ArrayList<>();
                    sysDeptUserService.getUserDeptListByUserId(item.getId()).forEach(data -> {
                        result.add(buildDeptParentTree(data.getDeptId()));
                        deptNameList.add(data.getDeptName());
                    });
                    item.setDeptNameList(deptNameList);
                    item.setDeptTreeList(result);
                }).collect(Collectors.toList());
    }

    @Override
    public List<SysUser> getDataPermissionPageList(SysUser sysUser) {
        checkStringLength(sysUser, true);
        return sysUserMapper.getDataPermissionPageList(sysUser)
                .parallelStream()
                .peek(item -> {
                    if (StringUtils.isNotBlank(item.getTag())) {
                        item.setTagList(Arrays.asList(item.getTag().split(",")));
                    }
                })
                .peek(item -> {
                    List<List<Long>> result = new ArrayList<>();
                    List<String> deptNameList = new ArrayList<>();
                    sysDeptUserService.getUserDeptListByUserId(item.getId()).forEach(data -> {
                        result.add(buildDeptParentTree(data.getDeptId()));
                        deptNameList.add(data.getDeptName());
                    });
                    item.setDeptNameList(deptNameList);
                    item.setDeptTreeList(result);
                }).collect(Collectors.toList());
    }

    /**
     * 构建用户所属部门父部门
     *
     * @param deptId
     * @return
     */
    private List<Long> buildDeptParentTree(Long deptId) {
        List<SysDept> deptList = sysDeptService.getList(new SysDept());
        Optional<SysDept> sysDeptOptional = deptList.stream().filter(item -> item.getId().equals(deptId)).findFirst();
        if (!sysDeptOptional.isPresent()) {
            return null;
        }
        List<Long> treeList = new ArrayList<>();
        treeList.add(sysDeptOptional.get().getId());
//        buildParentTree(sysDeptOptional.get().getParentId(), deptList, treeList);
        Collections.reverse(treeList);
        return treeList;
    }

    private void buildParentTree(String deptId, List<SysDept> deptList, List<Long> treeList) {
        Optional<SysDept> sysDeptOptional = deptList.stream().filter(item -> item.getId().equals(Long.valueOf(deptId))).findFirst();
        if (sysDeptOptional.isPresent()) {
            treeList.add(sysDeptOptional.get().getId());
//            buildParentTree(sysDeptOptional.get().getParentId(), deptList, treeList);
        }
    }

    @Override
    public SysUser getExitOneById(Long id) {
        WJAssert.notNull(id, "id参数错误");
        SysUser where = new SysUser();
        where.setStatus(EnumStatus.NEW.getCode());
        where.setId(id);
        return sysUserMapper.getOne(where);
    }

    @Override
    public void delete(Long id, String operator) {
        SysUser req = new SysUser();
        req.setId(id);
        req.setOperator(operator);
        req.setStatus(EnumStatus.DEL.getCode());
        updateSelectiveById(req);
    }

    @Override
    public SysUser login(SysUser sysUserReq) {
        WJAssert.limitMaxLength(sysUserReq.getUserName(), false, 100, "用户名不合法!");
        WJAssert.limitMaxLength(sysUserReq.getPassword(), false, 100, "密码不合法!");
        WJAssert.limitMaxLength(sysUserReq.getVerifyCode(), false, 100, "验证码不合法!");
        WJAssert.limitMaxLength(sysUserReq.getVerificationCodeToken(), false, 100, "验证码TOKEN不合法!");
        EvsApiLoginVO loginVO = new EvsApiLoginVO();
        loginVO.setName(sysUserReq.getUserName());
        loginVO.setPassword(sysUserReq.getPassword());
        loginVO.setVerificationCode(sysUserReq.getVerifyCode());
        loginVO.setVerificationCodeToken(sysUserReq.getVerificationCodeToken());
        HttpResponse httpResponse = evsHttpService.evsLogin(loginVO);
        JSONObject result = JSONObject.parse(httpResponse.body());
        if(!StringUtils.isEmpty(result.getString("code"))){
            checkLoginErrorNumByUsername(sysUserReq.getUserName());
            throw new BusinessException(result.getString("message"));
        }
        String header = httpResponse.header("Set-Cookie");
        String sessionValue = extractSessionValue(header);
        SysUser user = new SysUser();
        JSONObject userInfo = evsHttpService.getCurrentUserInfo(sessionValue);
        String redirect = userInfo.getString("redirect");
        if(!StringUtils.isEmpty(redirect)){
            return null;
        }
        String status = userInfo.getString("status");
        if(!status.equals("ENABLE")){
            throw new BusinessException("当前账户状态异常，请联系管理员！");
        }
        List<DeparmentInfoListVO> departmentInfoList = JSONArray.parseArray(userInfo.getString("departmentInfoList"), DeparmentInfoListVO.class);
        departmentInfoList.forEach(item -> {
            SysDept dept = sysDeptService.getOneById(item.getDepartmentId());
            if(!ObjectUtils.isEmpty(dept)){
                item.setDeptType(dept.getDeptType());
            }
        });
        user.setDepartmentInfoList(departmentInfoList);
        user.setToken(sessionValue);
        user.setStatus(0);
        user.setName(userInfo.getString("name"));
        user.setId(userInfo.getLong("id"));
        user.setTel(userInfo.getString("tel"));
        user.setType(userInfo.getString("type"));
        return user;
    }

    private String extractSessionValue(String input) {
        // 使用正则表达式提取SAAS_SESSION的值
        Pattern pattern = Pattern.compile("SAAS_SESSION=([^;]+)");
        Matcher matcher = pattern.matcher(input);
        // 检查是否有匹配
        if (matcher.find()) {
            // 返回匹配的值
            return matcher.group(1);
        } else {
            throw new BusinessException("未检出session");
        }
    }


//    @Override
//    public SysUser login(SysUser sysUserReq) {
//        WJAssert.limitMaxLength(sysUserReq.getUserName(), false, 100, "用户名不合法!");
//        WJAssert.limitMaxLength(sysUserReq.getPassword(), false, 100, "密码不合法!");
//        WJAssert.limitMaxLength(sysUserReq.getVerifyCode(), false, 100, "验证码不合法!");
//        WJAssert.notNull(sysUserReq.getVerifyCodeCurrentTime(), "验证码时间不合法!");
//        String verifyCodeCurrentTime = String.valueOf(sysUserReq.getVerifyCodeCurrentTime().getTime());
//        String code = verifyCodeService.getCacheCodeByTimestamp(verifyCodeCurrentTime);
//        //验证码判断
//        if (code == null || !sysUserReq.getVerifyCode().toUpperCase().equals(code)) {
//            throw new BusinessException("验证码错误");
//        }
//        checkLoginErrorNum(sysUserReq.getUserName());
//
//        SysUser sysUser = new SysUser();
//        sysUser.setPhone(sysUserReq.getUserName());
//        SysUser sysUserDB = sysUserMapper.getOne(sysUser);
//        if (Objects.isNull(sysUserDB) || !bCryptPasswordEncoder.matches(sysUserReq.getPassword(), sysUserDB.getPassword())) {
//            checkLoginErrorNumByUsername(sysUserReq.getUserName());
//            throw new BusinessException("账户不存在或密码错误");
//        }
//        if (Objects.equals(sysUserDB.getStatus(), EnumStatus.DEL.getCode())) {
//            throw new BusinessException("账户已删除");
//        }
//        verifyCodeService.clearCacheCodeByTimestamp(verifyCodeCurrentTime);
//        return sysUserDB;
//    }

    @Override
    public SysUser thirdLogin(String username, String password) {
        checkLoginErrorNum(username);

        SysUser sysUser = new SysUser();
        sysUser.setUserName(username);
        SysUser sysUserDB = sysUserMapper.getOne(sysUser);
        if (Objects.isNull(sysUserDB) || !bCryptPasswordEncoder.matches(password, sysUserDB.getPassword())) {
            checkLoginErrorNumByUsername(username);
            throw new BusinessException("用户名或密码错误");
        }
        if (Objects.equals(sysUserDB.getStatus(), EnumStatus.DEL.getCode())) {
            throw new BusinessException("账户已删除");
        }
        return sysUserDB;
    }

    /**
     * 检查错误次数
     *
     * @param username
     */
    private void checkLoginErrorNum(String username) {
        ValueOperations valueOperations = redisTemplate.opsForValue();
        String key = String.format(RedisKeys.SYS_USER_LOGIN_ERROR_NUM, username);
        Object errorNumObj = valueOperations.get(key);
        if (errorNumObj != null && Integer.parseInt(errorNumObj.toString()) > 4) {
            throw new BusinessException("错误次数过多，请过半小时后再试！");
        }
    }

    @Override
    public void updatePassword(String oldPassword, String newPassword, Long sysUserId) {
        SysUser sysUser = new SysUser();
        sysUser.setId(sysUserId);
        SysUser sysUserDB = sysUserMapper.getOne(sysUser);
        if (Objects.isNull(sysUserDB) || !bCryptPasswordEncoder.matches(oldPassword, sysUserDB.getPassword())) {
            throw new BusinessException("旧密码输入有误！");
        }
        sysUserDB.setPassword(bCryptPasswordEncoder.encode(newPassword));
        updateSelectiveById(sysUserDB);
    }

    @Override
    public void updatePhone(String phone, Long sysUserId) {
        SysUser sysUser = new SysUser();
        sysUser.setId(sysUserId);
        SysUser sysUserDB = sysUserMapper.getOne(sysUser);
        sysUserDB.setPhone(phone);
        updateSelectiveById(sysUserDB);
    }

    /**
     * 验证用户登陆错误次数
     *
     * @param username
     */
    @Override
    public void checkLoginErrorNumByUsername(String username) {
        String key = String.format(RedisKeys.SYS_USER_LOGIN_ERROR_NUM, username);
        ValueOperations valueOperations = redisTemplate.opsForValue();
        Object errorNumObj = valueOperations.get(key);
        int errorNum = 1;
        if (errorNumObj != null) {
            errorNum += Integer.parseInt(errorNumObj.toString());
        }
        valueOperations.set(key, String.valueOf(errorNum), 30, TimeUnit.MINUTES);
        if (errorNum > 4) {
            throw new BusinessException("错误次数过多，请过半小时后再试！");
        }
    }

    private boolean isEmailExist(SysUser sysUserReq) {
        return getOneByEmail(sysUserReq.getEmail()).map(item -> !item.getId().equals(sysUserReq.getId())).orElse(false);
    }

    private Boolean isUsernameExist(SysUser sysUserReq) {
        return getOneByUsername(sysUserReq.getUserName()).map(item -> !item.getId().equals(sysUserReq.getId())).orElse(false);
    }

    private Boolean isUserPhoneExist(SysUser sysUserReq) {
        return ObjectUtils.isEmpty(getOneByUserPhone(sysUserReq.getPhone()));
    }

    private Optional<SysUser> getOneByUsername(String username) {
        SysUser req = new SysUser();
        req.setUserName(username);
        return Optional.ofNullable(sysUserMapper.getOne(req));
    }

    private Optional<SysUser> getOneByUserPhone(String phone) {
        SysUser req = new SysUser();
        req.setPhone(phone);
        return Optional.ofNullable(sysUserMapper.getOne(req));
    }

    private Optional<SysUser> getOneByEmail(String email) {
        SysUser req = new SysUser();
        req.setEmail(email);
        return Optional.ofNullable(sysUserMapper.getOne(req));
    }

    /**
     * 参数校验注解默认使用的方法
     */
    private void checkStringLength(SysUser sysUser, boolean nullAble) {
        WJAssert.notNull(sysUser, "入参对象不能为空");
//        WJAssert.limitMaxLength(sysUser.getUserName(), nullAble, 100, "用户名不合法!");
//        WJAssert.limitMaxLength(sysUser.getPassword(), nullAble, 100, "密码不合法!");
//        WJAssert.limitMaxLength(sysUser.getPhone(), true, 20, "手机不合法!");
//        WJAssert.limitMaxLength(sysUser.getName(), true, 50, "姓名不合法!");
//        WJAssert.limitMaxLength(sysUser.getEmail(), true, 100, "邮箱不合法!");
//        WJAssert.limitMaxLength(sysUser.getOperator(), nullAble, 50, "操作人不合法!");
//        WJAssert.limitMaxLength(sysUser.getRemark(), true, 255, "备注不合法!");
    }
}
